#include <getopt.h>
#include "odump.h"

#define LONG_STR 30		// longitud maxima para nombres de archivo y delimiter

typedef struct {
	char mode;
	char input[LONG_STR];
	char output[LONG_STR];
	unsigned int length;
	char delimiter[LONG_STR];
} estado;

static void usage(void);
static void version(void);
static void setDefaults(estado* opt);
static int tryCopyString(char* src, char* dest, char* par);
static int parseCommandLine(int argc, char** argv, estado* opt);

int main(int argc, char** argv) {
	int exit_status = OK;
    estado opciones;
    struct odump_opts opts;  // version para el tp1
    setDefaults(&opciones);
    exit_status = parseCommandLine(argc, argv, &opciones);

	FILE* in = stdin;
	FILE* out = stdout;

	if(strlen(opciones.input) > 1)
		if((exit_status = !(in = fopen(opciones.input, "r"))))
			fprintf(stderr, "No se pudo abrir el archivo %s\n", opciones.input);

	if(strlen(opciones.output) > 1)
		if((exit_status = !(out = fopen(opciones.output, "w"))))
			fprintf(stderr, "No se pudo abrir el archivo %s\n", opciones.output);

	// realizo la copia y referenciacion entre los structs
	opts.length = opciones.length;
	opts.delim = opciones.delimiter;

	if(!exit_status) {
		if(opciones.mode == 'o')
			exit_status = odump_encode(fileno(in), fileno(out), &opts);
		else
			exit_status = odump_decode(fileno(in), fileno(out), &opts);
	}

	if(in)
		fclose(in);
	if(out)
		fclose(out);

	return exit_status;
}

static void usage() {
	printf("usage: tp0 -h, --help      	Print this information.\n");
	printf("       tp0 -V, --version   Print version and quit.\n");
	printf("       tp0 -0, --octal  	Octal dump mode: encode the input (binary file) in octal\n");
	printf("       	 					format (default).\n");
	printf("       tp0 -b, --binary 	Binary mode: decode the input (octal dump file) in binary\n");
	printf("       						format.\n");
	printf("       tp0 -i, --input 		Path to input file (defaults to stdin)\n");
	printf("       tp0 -o, --output 	Path to output file (defaults to stdout).\n");
	printf("       tp0 -l, --length		Number of octal bytes per line (line length) at the\n");
	printf("       		 				output in octal dump mode (0 = default = no limit).\n");
	printf("       tp0 -d, --delimiter	Delimiter between octal bytes. Could be a string, not only\n");
	printf("      						a single byte. Used as delimiter for octal output and input\n");
	printf("       						files (default: none).\n");
}

static void version() {
	printf("La version es 1.1\n");
}

static void setDefaults(estado* opt) {
	opt->mode = 'o';		// modo octal
	opt->input[0] = '\0';	// tomo entrada por stdin
	opt->output[0] = '\0';	// vuelco salida por el stdout
	opt->length = 0;		// infinitos bytes octales por linea
	opt->delimiter[0] = '\0'; // ningun delimitador entre bytes
}

static int tryCopyString(char* src, char* dest, char* par) {
	if(strlen(src) < LONG_STR) {
		strcpy(dest, src);
		return OK;
	}
	else {
		fprintf(stderr, "La long. del parametro <%s> supera los %d caracteres\n", par, LONG_STR);
		return FATAL_ERROR;
	}
}

static int parseCommandLine(int argc, char** argv, estado* opt) {

    /* Una cadena que lista las opciones cortas válidas */
    const char* const op_cortas = "hVObiold" ;

    /* Una estructura de varios arrays describiendo los valores largos */
    const struct option op_largas[] =
    {
      { "help",       0,  NULL,  'h'},
      { "version",    0,  NULL,  'V'},
      { "octal",      0,  NULL,  'O'},
      { "binary",     0,  NULL,  'b'},
      { "input",      0,  NULL,  'i'},
      { "output",     0,  NULL,  'o'},
      { "length",     0,  NULL,  'l'},
      { "delimiter",  0,  NULL,  'd'},        
      { NULL,         0,  NULL,   0 }
    };

	int ch, exit_status = OK;

	while (!exit_status && (ch = getopt_long(argc, argv, op_cortas, op_largas, NULL )) != -1)
		switch ((char) ch) {
		case 'h':
			usage();
			exit_status = ABORT;
			break;
		case 'V':
			version();
			exit_status = ABORT;
			break;
		case 'O':
			opt->mode = 'o';
			break;
		case 'b':
			opt->mode = 'b';
			break;
		case 'i':
			exit_status = tryCopyString(argv[optind], opt->input, "input file");
			break;
		case 'o':
			exit_status = tryCopyString(argv[optind], opt->output, "output file");
			break;
		case 'l':
			opt->length = atoi(argv[optind]);
			if(opt->length == 0 && strcmp(argv[optind], "0")) {
				fprintf(stderr, "La longitud de linea propuesta es invalida\n");
				exit_status = FATAL_ERROR;
			}
			break;
		case 'd':
			exit_status = tryCopyString(argv[optind], opt->delimiter, "delimiter");
			break;
		default:
			fprintf(stderr, "Parametro no valido: %c\n", ch);
			exit_status = FATAL_ERROR;
			break;
		}

	return exit_status;
}
